package cn.com.zzl.util;

import org.apache.commons.lang3.StringUtils;
import org.junit.Test;

import java.util.*;

/**
 * 说明：该工具类里面有很多方法都使用到了泛型T，
 * 使用泛型T需要注意的一点是：只对数据包装对象类型有效，对基本数据类型无效！
 * 因为只有Object及其子类才可以泛型化。
 * 说白了就是对Byte、Short、Integer、Long、Character、Float、Double、Boolean、User等对象类型有效果，
 * 对byte、short、int、long、char、float、double、boolean等基本数据类型没有效果。
 * Description：数组工具类
 * Created by 朱正磊 on 2017-05-27 0:20.
 */
public class ArrayUtil {

	/**
	 * @description 将数组转换成以逗号拼接的字符串，比如数组[1, 2, 3]转换成字符串是1,2,3的形式
	 * @param t 待转换的数组
	 * @return 返回以逗号拼接后的字符串
	 */
	public static <T> String arrayToStr(T[] t) {
		return arrayToStr(t, ",");
	}
	
	/**
	 * @description 将数组转换成以指定分隔符拼接的字符串，比如指定分隔符为:，那么数组[1, 2, 3]转换成字符串是1:2:3的形式
	 * @param t 待转换的数组
	 * @param regex 指定分隔符
	 * @return 返回以指定分隔符拼接后的字符串
	 */
	public static <T> String arrayToStr(T[] t, String regex) {
		return StringUtils.join(t, regex);
	}
	
	/**
	 * @description 将含有逗号分隔符的字符串转换成字符串数组
	 * @param str 含有逗号分隔符的字符串
	 * @return 返回字符串数组
	 */
	public static String[] strToArray(String str) {
		return strToArray(str, ",");
	}
	
	/**
	 * @description 将含有指定分隔符的字符串转换成字符串数组
	 * @param str 含有指定分隔符的字符串
	 * @param regex 指定分隔符
	 * @return 返回字符串数组
	 */
	public static String[] strToArray(String str, String regex) {
		return str.split(regex);
	}
	
	/**
	 * @description 字符串数组转Integer数组
	 * @param strArray 字符串数组
	 * @return 返回Integer数组
	 */
	public static Integer[] StrArrToIntArr(String[] strArr) {
		Integer[] intArr = new Integer[strArr.length];
	    for (int i = 0; i < strArr.length; i++) {
	    	intArr[i] = Integer.valueOf(strArr[i]);
	    }
	    return intArr;
	}
	
	/**
	 * @description 字符串数组转Long数组
	 * @param strArray 字符串数组
	 * @return 返回Long数组
	 */
	public static Long[] StrArrToLongArr(String[] strArr) {
		Long[] LongArr = new Long[strArr.length];
	    for (int i = 0; i < strArr.length; i++) {
	    	LongArr[i] = Long.valueOf(strArr[i]);
	    }
	    return LongArr;
	}

	/**
	 * @description 数组转字符串，格式无变化，比如数组[1, 2, 3]转换成字符串还是[1, 2, 3]的形式
	 * @param array
	 * @param <T>
	 * @return
	 */
	public static <T> String array2String(T[] t) {
		return Arrays.toString(t);
	}

	/**
	 * @Desc 数组转换为List，该方法不建议使用！
	 * @param array
	 * @param <T>
	 * @return
	 */
	public static <T> List<T> array2List(T[] t){
		return Arrays.asList(t);
	}
	
	/**
	 * 说明：使用可变数组作为入参
	 * 提醒：因为是可变并且非具体类型形式化参数，所以入参元素类型没有限制，因为如果是基本数据类型那么会自动装箱即封装为包装类型，
	 * 还有就是入参元素类型可以是任意类型，这个时候T就是? extends Object
	 * @description 数组转为ArrayList，使之可以增加或者删除集合元素
	 * @param t
	 * @return
	 */
	@SafeVarargs
	public static <T> ArrayList<T> asList(T... t) {
		if (t == null)
			return null;
		return new ArrayList<>(Arrays.asList(t));
	}
	
	/**
	 * 说明：使用可变数组作为入参
	 * 提醒：因为是可变并且非具体类型形式化参数，所以入参元素类型没有限制，因为如果是基本数据类型那么会自动装箱即封装为包装类型，
	 * 还有就是入参元素类型可以是任意类型，这个时候T就是? extends Object
	 * @description 数组转为ArrayList，使之可以增加或者删除集合元素
	 * @param t
	 * @return
	 */
	@SafeVarargs
	public static <T> ArrayList<T> toList(T... t) {
		if (t == null)
			return null;
		ArrayList<T> list = new ArrayList<>(t.length);//虽然构造参数的有无以及大小对程序无影响，但是t.length是最优选择
		boolean flag = Collections.addAll(list, t);
		if (flag == true)
			return list;
		return null;
	}

	/**
	 * 说明：使用常规（泛型）数组作为入参
	 * 警告：由于泛型限制，数组元素类型不可以是基本数据类型，必须是对象类型！
	 * @description 数组转为ArrayList，使之可以增加或者删除集合元素
	 * @param t
	 * @return
	 */
	public static <T> ArrayList<T> arrayToList(T[] t) {
		if (t == null)
			return null;
		ArrayList<T> list = new ArrayList<>(t.length);//虽然构造参数的有无以及大小对程序无影响，但是t.length是最优选择
		boolean flag = Collections.addAll(list, t);
		if (flag == true)
			return list;
		return null;
	}
	
	/**
	 * 说明：数组先转成ArrayList，然后又转成HashSet，性能开销比较大，不推荐这么做
	 * @description 判断数组是否包含特定值
	 * @param t 数组
	 * @param a 特定元素
	 * @return 如果数组包含特定元素返回true，否则返回false
	 */
	public static <T> boolean targetValueInArray(T[] t, T a) {
		Set<T> set = new HashSet<>(Arrays.asList(t));
		return set.contains(a);
	}
	
	/**
	 * 说明：代码高效，推荐这么做
	 * @description 判断数组是否包含特定值
	 * @param a 特定元素
	 * @param t 数组
	 * @return 如果数组包含特定元素返回true，否则返回false
	 */
	public static <T> boolean targetValueInArray(T a, T[] t) {
		boolean flag = false;
		for (T param : t) {
			if (a.equals(param)) {
				flag = true;
				break;
			}
		}
		return flag;
	}
	
	/**
	 * 说明：T[] toArray(T[] a)方法比较完美，可以方便我们使用（其中a的长度最好设置为集合的长度）！
	 * @description 该方法作用不大，其实直接调用List的toArray()方法即可。
	 * @param list 对象集合
	 * @return 返回Object数组
	 */
	public static <T> Object[] list2Array(List<T> list) {
		return list.toArray();
	}

	/**
	 * @Desc 给数组所有元素统一赋值
	 * @param array
	 * @param value
	 * @param <T>
	 * @return
	 */
	public static <T> T[] setValueForArrayAll(T[] array, Object value){
		Arrays.fill(array, value);
		return array;
	}

	/**
	 * 注意：包括开始索引但不包括结束索引
	 * @Desc 给数组部分元素统一赋值，可以是一个元素，也可以是多个元素
	 * @param array
	 * @param start 开始索引位置
	 * @param end 结束索引位置
	 * @param value
	 * @param <T>
	 * @return
	 */
	public static <T> T[] setValueForArrayPart(T[] array, int start, int end, Object value){
		Arrays.fill(array, start, end, value);
		return array;
	}

	/**
	 * @Desc 对数组所有的元素进行升序排序
	 * @param array
	 * @param <T>
	 * @return
	 */
	public static <T> T[] sortForArrayAll(T[] array){
		Arrays.sort(array);
		return array;
	}

	/**
	 * 注意：包括开始索引但不包括结束索引
	 * @Desc 对数组的部分元素进行升序排序
	 * @param array
	 * @param start 开始索引位置
	 * @param end 结束索引位置
	 * @param <T>
	 * @return
	 */
	public static <T> T[] sortForArrayPart(T[] array, int start, int end){
		Arrays.sort(array, start, end);
		return array;
	}

	/**
	 * 注意：泛型T指的是对象数据类型，基本数据类型不符合
	 * @Desc 比较两个数组是否相等，数组元素个数和元素的值都相等
	 * @param array1
	 * @param array2
	 * @param <T>
	 * @return
	 */
	public static <T> boolean equalsArray(T[] array1, T[] array2){
		return Arrays.equals(array1, array2);
	}

	/**
	 * @Desc 查找某个元素在数组中的索引位置（索引从0开始）
	 * @param array
	 * @param element
	 * @param <T>
	 * @return 如果数组中存在该元素返回该元素在数组中的索引位置，如果不存在返回一个负数
	 */
	public static <T> int searchElementIndexInArray(T[] array, Object element){
		Arrays.sort(array);//先进行升序排序再进行查找
		return Arrays.binarySearch(array, element);
	}

	/**
	 * @Desc 判断一个字符串元素在字符串数组中出现了几次（次数）
	 * @param baseStr 字符串元素
	 * @param strings 字符串数组
	 * @return
	 */
	public static int eleSumInArray(String baseStr, String[] strings){
		if(null == baseStr || "".equals(baseStr) || null == strings){//其实"".equals(baseStr)等同于baseStr.length() == 0
			return 0;
		}
		int sum = 0;
		for(String string : strings){
			boolean result = baseStr.equals(string);
			sum = result ? ++sum : sum;//三元表达式，这里只能使用++sum，不能使用sum++（如果使用sum++，sum会始终为0）
		}
		return sum;
	}

	/**
	 * @Desc List<Object[]>转字符串
	 * @param list
	 * @return
	 */
	public static String listObjectArrayToString(List<Object[]> list){
		String string1 = "";
		for(int i=0; i<list.size(); i++){
			Object[] objects = list.get(i);
			String string2 = "";
			for(int j=0; j<objects.length; j++){
				string2 += StringUtils.stripToEmpty(objects[j].toString()) + ", ";
			}
			string1 += StringUtils.stripToEmpty(string2) + "; ";
		}
		return string1;
	}

	/**
	 * @Desc 把数组转换成TreeSet方便判断(数组元素不能重复，即使重复在Set里面也只会存在一个)
	 * @param args
	 * @return
	 */
	public static TreeSet<String> arrayToTreeSet(String[] args){
		TreeSet<String> result = new TreeSet<String>();
		if(null == args){
			return result;
		}
		for(String string : args){
			result.add(string);
		}
		return result;
	}

	/**
	 * @Desc 把数组转换成LinkedHashSet方便判断(数组元素不能重复，即使重复在Set里面也只会存在一个)
	 * @param args
	 * @return
	 */
	public static LinkedHashSet<String> arrayToLinkedHashSet(String[] args){
		LinkedHashSet<String> result = new LinkedHashSet<String>();
		if(null == args){
			return result;
		}
		for(String string : args){
			result.add(string);
		}
		return result;
	}
	
	/**
	 * @description 字符串数组拼接字符串
	 * @param start 头字符串
	 * @param end 尾字符串
	 * @param strArr 待拼接字符串数组
	 * @return 返回拼接后的字符串
	 */
	public static String strArr2String(String start, String end, String... strArr) {
		StringBuffer sb = new StringBuffer();
		if (start != null)
			sb.append(start);
		for (String s : strArr)
			sb.append(s);
		if (end != null)
			sb.append(end);
		return sb.toString();
	}

	@SuppressWarnings("unused")
	private static <T> void output(T[] array){
		if(array != null){
			for (int i = 0; i < array.length; i++){
				System.out.println(array[i]);
			}
		}
	}
	
	@Test
	public void test() {
		Integer[] integers = new Integer[]{1,2,3};
		
		List<Integer> integerList = array2List(integers);
		System.out.println(integerList);//[1, 2, 3]
		
		Object[] ints = list2Array(integerList);
		System.out.println(array2String(ints));//[1, 2, 3]
		
		String arrStr_1 = arrayToStr(ints, ":");
		System.out.println(arrStr_1);//1:2:3
		
		String arrStr_2 = arrayToStr(ints);
		System.out.println(arrStr_2);//1,2,3
		
		String[] strArr_1 = strToArray(arrStr_1, ":");
		System.out.println(array2String(strArr_1));//[1, 2, 3]
		
		String[] strArr_2 = strToArray(arrStr_2);
		System.out.println(array2String(strArr_2));//[1, 2, 3]
	}

	public static void main(String[] args) {

//		Integer[] integers = new Integer[]{1,2,3};
//
//		//数组转换为List
//		List<Integer> integerList = array2List(integers);
//		System.out.println(integerList);//[1, 2, 3]
//
//		//List转换为数组
//		Integer[] ints = list2Array(integerList);
//		System.out.println(array2String(ints));//[1, 2, 3]
//		System.out.println(ints.length);//3
//		System.out.println(ints[0]);//1
//		System.out.println(ints[1]);//2
//		System.out.println(ints[2]);//3

		//**********************************************************************************************//

		Object[] array = new Object[10];//数组长度为10

		//1、给数组赋值：通过fill方法。
		//Arrays.fill(array,5);//表示给数组里面的所有元素赋值为5，如果索引位置元素已存在会被覆盖掉。
		array = setValueForArrayAll(array,5);//这里有没有返回值都一样
		//output(array);

		//给数组元素赋值，包括开始索引但不包括结束索引
		//这里有没有返回值都一样
		//Arrays.fill(array, 0, 1, "朱");
		setValueForArrayPart(array, 0, 1, "朱");
		//Arrays.fill(array, 1, 2, "赵");
		setValueForArrayPart(array, 1, 2, "赵");
		//Arrays.fill(array, 2, 3, "钱");
		setValueForArrayPart(array, 2, 3, "钱");
		//Arrays.fill(array, 3, 4, "孙");
		setValueForArrayPart(array, 3, 4, "孙");
		//Arrays.fill(array, 4, 5, "李");
		setValueForArrayPart(array, 4, 5, "李");
		//Arrays.fill(array, 5, 6, "9");
		setValueForArrayPart(array, 5, 6, "9");
		//Arrays.fill(array, 6, 7, "5");
		setValueForArrayPart(array, 6, 7, "5");
		//Arrays.fill(array, 7, 8, "abc");
		setValueForArrayPart(array, 7, 8, "abc");
		//Arrays.fill(array, 8, 9, "zxc");
		setValueForArrayPart(array, 8, 9, "zxc");
		//Arrays.fill(array, 9, 10, "*&@");
		setValueForArrayPart(array, 9, 10, "*&@");
		//output(array);

		/*
			朱
			赵
			钱
			孙
			李
			9
			5
			abc
			zxc
        	*&@
        */

		//2、对数组排序：通过sort方法，按升序。
		//Arrays.sort(array);
		//sortForArrayAll(array);
		//output(array);

		/*
			*&@
			5
			9
			abc
			zxc
			孙
			朱
			李
			赵
			钱
        */

		//对数组的部分元素进行排序，包括开始索引但不包括结束索引
		//Arrays.sort(array, 2, 7);
		//sortForArrayPart(array, 2, 7);
		//output(array);

		/*
			朱
			赵
			5
			9
			孙
			李
			钱
			abc
			zxc
			*&@
        */

		//3、比较数组：通过equals方法比较数组中元素值是否相等。

		Integer[] intArray1 = new Integer[]{1,2,3,4,5};
		Integer[] intArray2 = {1,2,3,4,5};
		Integer[] intArray3 = new Integer[5];
		intArray3[0] = 2;
		intArray3[1] = 3;
		intArray3[2] = 4;
		intArray3[3] = 5;
		intArray3[4] = 6;

		//boolean flag = Arrays.equals(intArray1, intArray2);
		boolean flag = equalsArray(intArray1, intArray2);
		System.out.println(flag);//true

		//flag = Arrays.equals(intArray1, intArray3);
		flag = equalsArray(intArray1, intArray3);
		System.out.println(flag);//false

		//flag = Arrays.equals(intArray2, intArray3);
		flag = equalsArray(intArray2, intArray3);
		System.out.println(flag);//false


		//4、查找数组元素：通过binarySearch方法能对排序好的数组进行二分查找法操作。

		//在使用binarySearch方法查找之前先使用sort排序
		//Arrays.sort(array);

		//int index = Arrays.binarySearch(array,"5");
		int index = searchElementIndexInArray(array,"5");
		System.out.println(index);//1，存在返回索引位置，索引从0开始

		//index = Arrays.binarySearch(array,"666hhh");
		index = searchElementIndexInArray(array,"666hhh");
		System.out.println(index);//-3，不存在就返回一个负数，这里返回的是-3

		//**********************************************************************************************//

//		String baseStr = "abc";
//		String[] strings = {"zxc","abc","mnb","abc","kij","opl","abc"};
//		int sum = eleSumInArray(baseStr,strings);
//		System.out.println(sum);//打印结果为3
//
//		Object[] object1 = new Object[]{1,2,3,4,5};
//		Object[] object2 = new Object[]{"a","b","c","d","e"};
//		Object[] object3 = new Object[]{true,2,false,"1234"};
//		List<Object[]> list = new ArrayList<Object[]>();
//		list.add(object1);
//		list.add(object2);
//		list.add(object3);
//		String str = listObjectArrayToString(list);
//		//1, 2, 3, 4, 5,; a, b, c, d, e,; true, 2, false, 1234,;
//		System.out.println(str);
//
//		String[] arrayStr = {"123","abc","3we","dr45","fgh67","asc43","fr5de","123"};
//		System.out.println(arrayToTreeSet(arrayStr));//[123, 3we, abc, asc43, dr45, fgh67, fr5de]
//		System.out.println(arrayToLinkedHashSet(arrayStr));//[123, abc, 3we, dr45, fgh67, asc43, fr5de]

	}

}
